package services

import (
	"encoding/json"
	E "errors"
	"fmt"
	"io/ioutil"
	"path"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/errors"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"peilian-api/utils/function"
	"peilian-api/utils/sdk"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type PacticeService struct {
}

type SessionReqUser struct {
	UserId    uint   `json:"user_id" validate:"required"`
	UserName  string `json:"user_name"`
	UserPhone string `json:"user_phone"`
}

type SessionReq struct {
	CareerId uint             `json:"career_id" validate:"required"`
	Users    []SessionReqUser `json:"users" validate:"required"`
}

type SessionResp struct {
	Id uint `json:"id"`
}

type UserSessionInfo struct {
	UserId    uint `json:"user_id"`
	SessionId uint `json:"session_id"`
}

type SessionInfoResp struct {
	Data           []UserSessionInfo `json:"user_session"`
	QuestionId     uint              `json:"question_id"`
	QuestionText   string            `json:"question_text"`
	QuestionOrigin string            `json:"question_origin"`
	PcVideo        string            `json:"pc_video"`
	Video          string            `json:"video"`
}

// 获取该用户的面试次数
func GetOnTrailTimes(ctx *gin.Context) int64 {
	uid, ok := ctx.Get("uid")
	if !ok || uid == 0 {
		return 0
	}
	u, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return 0
	}
	return int64(u.TrailCount)
}

// 创建考试场次
func PracticeCreate(ctx *gin.Context, careerId uint) (int64, error) {
	var session tables.Session
	uid, ok := ctx.Get("uid")
	if !ok || uid == 0 {
		return 0, E.New("fetch uid fail")
	}

	// 根据用户获取用户信息
	u, err := tables.GetUserInfoById(int(uid.(uint)))
	if err != nil {
		return 0, E.New("fetch user fail")
	}
	if u.ID == 0 {
		return 0, E.New("user is not exsit")
	}
	if u.Type == tables.UserTypeAnonymous && u.TrailCount <= 0 && u.IsMember != 1 {
		return -1, E.New("您需要开通会员")
	}

	// if u.Type == tables.UserTypeNormal {
	// 	sc := variable.DB.Redis.Get(fmt.Sprintf(common.RedisAgentIsTimeoutKey, u.AgentID))
	// 	str, err := sc.Result()
	// 	if err != nil {
	// 		return -1, E.New("您的服务已到期")
	// 	}
	// 	endStamp, _ := strconv.ParseInt(str, 10, 64)
	// 	if endStamp-time.Now().Unix() <= 0 {
	// 		return -1, E.New("您的服务已到期")
	// 	}
	// }

	// 根据careerId 获取 career信息
	career, err := tables.GetCareerInfoById(careerId)
	if err != nil || career.ID == 0 {
		return 0, fmt.Errorf("该职业面试不存在")
	}

	session.UserID = uid.(uint)
	session.CareerID = careerId
	session.School = u.School
	session.Academy = u.Academy
	session.Profession = u.Profession
	session.Education = u.Education
	session.Grade = u.Grade
	session.UserName = u.Name
	session.UserPhone = u.Phone
	session.CareerName = career.Name
	session.TrainAt = time.Now()
	session.Type = u.Type
	session.Sno = u.Sno
	session.IsSync = u.IsSync

	d := variable.DB.MySQL.Create(&session)
	if d.Error != nil {
		return 0, fmt.Errorf("插入失败")
	}
	variable.DB.Redis.Del(fmt.Sprintf(common.RedisUserInfoKey, uid))
	return int64(session.ID), nil
}

func (ps *PacticeService) CreateSession(ctx *gin.Context, req SessionReq) (*SessionInfoResp, error) {
	var out = &SessionInfoResp{}
	if req.CareerId == 0 {
		return nil, fmt.Errorf("参数错误")
	}
	// 根据careerId 获取 career信息
	career, err := tables.GetCareerInfoById(req.CareerId)
	if err != nil || career.ID == 0 {
		return nil, fmt.Errorf("该职业面试不存在")
	}

	for _, user := range req.Users {
		var session tables.Session
		// 根据用户获取用户信息
		u, err := tables.GetUserInfoById(int(user.UserId))
		if err != nil {
			return nil, E.New("fetch user fail")
		}
		if u.ID == 0 {
			return nil, E.New("user is not exsit")
		}
		session.UserID = user.UserId
		session.CareerID = req.CareerId
		session.School = u.School
		session.Academy = u.Academy
		session.Profession = u.Profession
		session.Education = u.Education
		session.Grade = u.Grade
		session.UserName = u.Name
		session.UserPhone = u.Phone
		session.CareerName = career.Name
		session.TrainAt = time.Now()
		session.Type = u.Type
		session.Sno = u.Sno
		session.IsSync = u.IsSync

		d := variable.DB.MySQL.Create(&session)
		if d.Error != nil {
			return nil, fmt.Errorf("插入失败")
		}
		one := UserSessionInfo{SessionId: session.ID, UserId: u.ID}
		out.Data = append(out.Data, one)
	}

	//获取面题的总题目
	var questions []tables.CustomCareer

	subQuery := variable.DB.MySQL.Model(&tables.CustomCareer{}).Preload("Question").
		Where("career_id=?", req.CareerId).Find(&questions).Order("tag")

	if err := subQuery.Error; err != nil {
		return nil, fmt.Errorf("获取题目失败：%+v", err)
	}
	if len(questions) == 0 {
		return nil, fmt.Errorf("题库中没有定义题目")
	}
	question := questions[0].Question
	out.QuestionId = question.ID
	out.QuestionText = question.Text
	out.Video = question.Video
	out.PcVideo = question.PcVideo
	out.QuestionOrigin = question.QuestionOrigin

	return out, nil
}

func ExtractFormFile(c *gin.Context) (string, *errors.ErrRes) {
	_, f, err := c.Request.FormFile("photo")
	if err != nil {
		return "", errors.NewErrInfo(20001, fmt.Errorf("图像数据获取失败, "+err.Error()))
	}
	fileSuffix := map[string]bool{
		".png":  true,
		".jpg":  true,
		".gif":  true,
		".jpeg": true,
	}
	fileExt := strings.ToLower(path.Ext(f.Filename))
	if _, ok := fileSuffix[fileExt]; !ok {
		return "", errors.NewErrInfo(20002, fmt.Errorf("上传失败, 文件格式 "+fileExt+" 不合法"))
	}

	fileName := function.CreateMD5(fmt.Sprintf("%s%s", f.Filename, time.Now().String())) + fileExt

	filePath := common.PhotoPath + fileName

	savePath := variable.Config.UfsInfo.BucketPrefix + filePath
	f2, err := f.Open()
	if err != nil {
		return "", errors.NewErrInfo(20003, fmt.Errorf("图像异常, "+err.Error()))
	}
	fileData, err := ioutil.ReadAll(f2)
	if err != nil {
		return "", errors.NewErrInfo(20004, fmt.Errorf("图像不存在, "+err.Error()))
	}

	err = sdk.IOput(fileData, savePath, "")
	if err != nil {
		return "", errors.NewErrInfo(20005, fmt.Errorf("上传IO失败, "+err.Error()))
	}
	return filePath, nil
}

// UpdatePhoto 上传图片
func UpdatePhoto(ctx *gin.Context, sid uint) *errors.ErrRes {
	filePath, err := ExtractFormFile(ctx)
	if err != nil {
		return err
	}
	// 将路径更新到对应的session
	var session tables.Session
	session.UserPhoto = filePath
	if result := variable.DB.MySQL.Where("id = ?", sid).Updates(&session).Limit(1); result.Error != nil {
		return errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf(result.Error.Error()))
	}
	return nil
}

// 根据type获取视频
func GetMerginVideo(typ string) ([]model.VideoResp, *errors.ErrRes) {
	var out []model.VideoResp
	videos, err := new(tables.MarginVideo).GetMerginVideoByType(typ)
	if err != nil {
		return out, errors.NewErrInfo(errors.DatabaseMysqlErrorCode, err)
	}
	for _, v := range videos {
		var vr model.VideoResp
		vr.Video = v.Video
		vr.Text = v.Text
		vr.Type = v.Type
		vr.PcVideo = v.PcVideo
		out = append(out, vr)
	}
	return out, nil
}

// 根据session 获取第一条面试问题的id
func GetVideoUrlBySessionId(ctx *gin.Context, sid uint) (video model.VideoResp, err error) {
	var session tables.Session
	var question tables.Question
	session.ID = sid
	d := variable.DB.MySQL.Model(&session).Where("id = ?", sid).First(&session)
	err2 := d.Error
	if E.Is(err2, gorm.ErrRecordNotFound) {
		return video, fmt.Errorf(err.Error())
	}
	d2 := variable.DB.MySQL.Model(&question).Where("career_id = ?", session.CareerID).First(&question).Limit(1)
	err3 := d2.Error
	if E.Is(err3, gorm.ErrRecordNotFound) {
		return video, fmt.Errorf(err.Error())
	}
	video.Video = question.Video
	video.Text = question.Text
	return video, nil
}

// 增加用户的面试次数
func (p *PacticeService) TrainCountAdd(ctx *gin.Context, msr model.ShareRes) *errors.ErrRes {
	// 1. 判断用户是否是游客账户
	uid := msr.UserId
	userObj := new(tables.User)
	whereStr := fmt.Sprintf("id = %d", uid)
	usList, _ := userObj.GetUsersByWhereStr(whereStr, "*")
	if len(usList) == 0 {
		return errors.NewErrInfo(10001, fmt.Errorf("您的账户出现异常，请联系管理员"))
	}
	var whereMap map[string]interface{}
	whereMap = make(map[string]interface{}, 0)
	whereMap["share_click_count"] = usList[0].ShareClickCount + 1
	if _, err := userObj.UpdateUserInfoByWhereStr(whereMap, whereStr); err != nil {
		return errors.NewErrInfo(10001, fmt.Errorf("操作异常，请联系管理员"))
	}
	// 记录分享人的信息：分享时间，分享人ID
	shareLog := tables.UserShareLog{UserID: uint(uid), ShareTimeStamp: msr.ShareTime}
	if err := variable.DB.MySQL.Where(&shareLog).FirstOrCreate(&shareLog).Error; err != nil {
		return errors.NewErrInfo(10001, fmt.Errorf("分享日志记录操作异常，请联系管理员"))
	}
	variable.DB.MySQL.Model(&shareLog).Update("click_count", shareLog.ClickCount+1)
	// 2. 判断该用户今天是否有已经增加
	sc := variable.DB.Redis.Get(fmt.Sprintf(common.RedisShareIsKey, uid))
	s, _ := sc.Result()
	if s != "" && s == "true" {
		fmt.Println("今日已经分享过")
		return nil
	}
	// 3. 判断该用户是否是分享成功
	shareStatus := msr.ShareStatus
	if shareStatus == 1 {
		return errors.NewErrInfo(10001, fmt.Errorf("您分享未成功，未获取试用次数"))
	}
	// 4. 更新数据库trail_count 加 1
	whereMap["trail_count"] = usList[0].TrailCount + 1
	if _, err := userObj.UpdateUserInfoByWhereStr(whereMap, whereStr); err != nil {
		return errors.NewErrInfo(10001, fmt.Errorf("操作异常，请联系管理员"))
	}
	// 5. 更新成功后，写入redis
	// 获取当前时间
	timeStr := time.Now().Format("2006-01-02")
	t, _ := time.ParseInLocation("2006-01-02 15:04:05", timeStr+" 23:59:59", time.Local)
	stamp := t.Unix() - time.Now().Unix()
	variable.DB.Redis.Set(fmt.Sprintf(common.RedisShareIsKey, uid), "true", time.Duration(stamp)*time.Second)
	return nil
}

// 获取分享文案
func (p *PacticeService) GetShareCopywriting(ctx *gin.Context) (model.ShareWriting, *errors.ErrRes) {
	shareWriting := model.ShareWriting{}
	cc, _ := new(tables.CommonConf).FetchByConfKey(tables.ConfKeyShareWriting)
	if len(cc) != 0 {
		confValue := cc[0].ConfValue
		json.Unmarshal([]byte(confValue), &shareWriting)
	}
	return shareWriting, nil
}

// 获取分享文案
func (p *PacticeService) GetShareCarousel(ctx *gin.Context, key string) ([]model.ShareCarousel, *errors.ErrRes) {
	shareCarousel := []model.ShareCarousel{}
	// _, ok := ctx.Get("uid")
	// if !ok {
	// 	return shareCarousel, errors.NewErrInfo(401, fmt.Errorf("用户登录异常"))
	// }
	cc, _ := new(tables.CommonConf).FetchByConfKey(key)
	if len(cc) != 0 {
		confValue := cc[0].ConfValue
		json.Unmarshal([]byte(confValue), &shareCarousel)
	}
	return shareCarousel, nil
}

func (p *PacticeService) GetGreeting(ctx *gin.Context) (map[string][]string, *errors.ErrRes) {
	var out map[string][]string
	out = make(map[string][]string, 0)
	cc, _ := new(tables.CommonConf).FetchByConfKey(tables.ConfKeyGreeting)
	if len(cc) != 0 {
		confValue := cc[0].ConfValue
		json.Unmarshal([]byte(confValue), &out)
	}
	return out, nil
}
